package com.sharp.authorization.service.auth.impl;

import com.sharp.authorization.constants.param.AccountParam;
import com.sharp.authorization.constants.token.TokenItemConst;
import com.sharp.authorization.dao.common.entity.AcUser;
import com.sharp.authorization.dao.common.repository.AcUserService;
import com.sharp.authorization.service.auth.IotAuthService;
import com.sharp.authorization.service.token.TokenService;
import com.sharp.authorization.vo.iot.IotBaseReq;
import com.sharp.authorization.vo.iot.IotBaseResp;
import com.sharp.authorization.vo.iot.IotFetchTokenResp;
import com.sharp.authorization.vo.iot.IotTokenConfirmReq;
import com.sharp.kernel.constant.CommonConst;
import com.sharp.kernel.constant.db.SqlConstant;
import com.sharp.kernel.constant.err.ErrCodeSys;
import com.sharp.framework.service.cache.FrameCacheService;
import com.sharp.kernel.utils.DateUtil;
import com.sharp.framework.utils.HttpUtil;
import com.sharp.kernel.utils.StringUtil;
import com.sharp.kernel.validate.Validator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * Title: IotAuthServiceImpl
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2019/10/3 17:00
 * @description Project Name: Grote
 * @Package: com.srct.service.account.service.impl
 */
@Slf4j
@Service
public class IotAuthServiceImpl implements IotAuthService {

    @Resource
    private AcUserService acUserService;
    @Resource
    private TokenService tokenService;
    @Resource
    private FrameCacheService frameCacheService;

    @Override
    public void validate(HttpServletRequest request, HttpServletResponse response) {
        String url = request.getRequestURI();
        String method = request.getMethod();
        String queryString = request.getQueryString();
        IotBaseReq iotBaseReq = HttpUtil.getParamMap(request, IotBaseReq.class);
        traceRequest(method, url, queryString, iotBaseReq);
        validateToken(iotBaseReq);
    }

    private void traceRequest(String method, String url, String queryString, IotBaseReq iotInfo) {
        log.info("[{}] {}?{} -> {}-{}}", method, url, queryString, iotInfo.getDeviceId(), iotInfo.getToken());
    }

    private void validateToken(IotBaseReq iotBaseReq) {
        String token = iotBaseReq.getToken();
        Validator.assertTrue(tokenService.isTokenExist(token), ErrCodeSys.SYS_SESSION_TIME_OUT, "token");
        String deviceId = tokenService.getItem(token, TokenItemConst.IOT_DEVICE_ID.name(), String.class);
        Validator.assertTrue(StringUtils.equals(iotBaseReq.getDeviceId(), deviceId), ErrCodeSys.SYS_ERR_MSG,
                "device id is not matched");
        tokenService.setToken(token);
        String needCleanOldToken = tokenService.getItem(TokenItemConst.IOT_NEED_CLEAN_OLD_TOKEN.name(), String.class);
        if (StringUtils.isNotEmpty(needCleanOldToken)) {
            tokenService.removeToken(needCleanOldToken);
        }
    }

    @Override
    public String getTokenUpdateSwitch() {
        Date refreshTime = tokenService.getItem(TokenItemConst.IOT_TOKEN_NEED_UPDATE_TIMESTAMP.name(), Date.class);
        String deviceId = tokenService.getItem(TokenItemConst.IOT_DEVICE_ID.name(), String.class);
        String updateSwitch = CommonConst.NO;
        if (refreshTime == null) {
            return updateSwitch;
        }
        updateSwitch = new Date().after(refreshTime) ? CommonConst.YES : CommonConst.NO;
        if (StringUtil.convertSwitch(updateSwitch)) {
            String token = tokenService.buildIotToken(deviceId);
            tokenService.putItem(TokenItemConst.IOT_UPDATE_TOKEN.name(), token);
        }
        return updateSwitch;
    }

    @Override
    public IotFetchTokenResp fetchToken(IotBaseReq iotBaseReq) {
        AcUser user = acUserService.getUserByGuid(iotBaseReq.getToken());
        String token;
        if (user != null) {
            Validator.assertNo(user.getStatus(), ErrCodeSys.SYS_ERR_MSG, "please contact admin to re-init device");
            token = buildNewUserToken(iotBaseReq);
        } else {
            user = acUserService.getUserByGuid(iotBaseReq.getDeviceId());
            if (user != null) {
                token = tokenService.getItem(iotBaseReq.getToken(), TokenItemConst.IOT_UPDATE_TOKEN.name(), String.class);
                Validator.assertNotBlank(token, ErrCodeSys.SYS_ERR_MSG, "Dont need update token");
            } else {
                token = buildNewUserToken(iotBaseReq);
            }
        }
        return buildIotFetchTokenResp(token);
    }

    private String buildNewUserToken(IotBaseReq iotBaseReq) {
        String token = tokenService.buildIotToken(iotBaseReq.getDeviceId());
        fillUpdateToken(token);
        tokenService.putItem(token, TokenItemConst.IOT_DEVICE_ID.name(), iotBaseReq.getDeviceId());
        tokenService.putItem(token, TokenItemConst.IOT_UPDATE_TOKEN.name(), token);
        return token;
    }

    private IotFetchTokenResp buildIotFetchTokenResp(String token) {
        IotFetchTokenResp resp = new IotFetchTokenResp();
        resp.setAuthToken(token);
        resp.setTokenUpdateSwitch(CommonConst.NO);
        return resp;
    }

    private void fillUpdateToken(String token) {
        int updatedExpired = frameCacheService.getParamInt(AccountParam.IOT_TOKEN_UPDATE_EXPIRED);
        Date updateStamp = DateUtil.addSeconds(new Date(), updatedExpired);
        tokenService.putItem(token, TokenItemConst.IOT_TOKEN_NEED_UPDATE_TIMESTAMP.name(), updateStamp);
    }

    @Override
    public IotBaseResp confirm(IotTokenConfirmReq tokenConfirmReq) {
        String oldToken = tokenService.getToken();
        String updateToken = tokenService.getItem(TokenItemConst.IOT_UPDATE_TOKEN.name(), String.class);
        Validator.assertTrue(StringUtils.equals(tokenConfirmReq.getTokenConfirmed(), updateToken),
                ErrCodeSys.SYS_ERR_MSG, "confirmed token is not matched");
        updateUserLoginTime(tokenConfirmReq.getDeviceId());
        tokenService.setTokenValue(updateToken, tokenService.getTokenValue());
        fillUpdateToken(updateToken);
        tokenService.putItem(updateToken, TokenItemConst.IOT_DEVICE_ID.name(), tokenConfirmReq.getDeviceId());
        replaceUpdateToken(oldToken, updateToken);
        return new IotBaseResp();
    }

    private void updateUserLoginTime(String guid) {
        AcUser user = acUserService.getUserByGuid(guid);
        if (user == null) {
            user = new AcUser();
            user.setGuid(guid);
            user.setLoginId(guid);
            user.setValid(SqlConstant.VALID);
        }
        user.setLastLoginTime(new Date());
        user.setStatus(CommonConst.YES);
        acUserService.insertOrUpdateSelective(user);
    }

    private void replaceUpdateToken(String oldToken, String updateToken) {
        if (!StringUtil.equals(tokenService.getToken(), updateToken)) {
            tokenService.putItem(updateToken, TokenItemConst.IOT_NEED_CLEAN_OLD_TOKEN.name(), oldToken);
            tokenService.setToken(updateToken);
        }
    }

}
